//
//  AppDelegate.m
//  me.huanghai.shanghanlun
//
//  Created by hh on 16/10/11.
//  Copyright © 2016年 hh. All rights reserved.
//

#import "AppDelegate.h"

@interface AppDelegate ()
{
    NSRegularExpression *reg; // 去除$a{}标记的正则表达式
}
@end

@implementation AppDelegate
static AppDelegate *me;

+ (instancetype)sharedApp
{
    return me;
}

- (void)initData
{
    [self initMetaData];
    [self initItems];
}

- (void)initItems
{
    NSBundle *b = [NSBundle mainBundle];
    NSString *path = [b pathForResource:@"shangHan_data" ofType:@"json"];
    NSString *json = [[NSString alloc] initWithContentsOfFile:path encoding:NSUTF8StringEncoding error:nil];
    NSArray<NSDictionary *> *tmp = [NSJSONSerialization JSONObjectWithData:[json dataUsingEncoding:NSUTF8StringEncoding] options:NSJSONReadingAllowFragments error:nil];
    _itemData = [tmp map:^SectionData *(NSDictionary *obj, NSInteger idx) {
        return [[SectionData alloc] initWithDictionary:obj];
    }];
}

- (void)initMetaData
{
    /*
     $n{...} 条文序号
     $f{...} 方名
     $a{...} 内嵌注释
     $m{...} 药味总数
     $s{...} 药煮法开头的“上...味"
     * $u{...} 药名
     * $v{...} 药名，但是不显示本经内容
     * $w{...} 药量
     * $q{...} 方名前缀（千金，外台） 不允许嵌套使用
     * $h{...} 隐藏的方名（暂时只用于标记方名)
     已支持嵌套使用
     */
    HH2SearchConfig *config = [HH2SearchConfig sharedConfig];
    self.colorDict = @{
                       @"n" : config.nColor,
                       @"f" : config.fangColor,
                       @"a" : config.commentColor,
                       @"m" : config.yaoNumColor,
                       @"s" : config.zhufaNumColor,
                       @"u" : config.yaoColor,
                       @"v" : config.yaoColor,
                       @"w" : config.yaoAmountColor,
                       @"q" : config.fangPrefixColor,
                       @"h" : [UIColor blackColor],
                       @"x" : [UIColor orangeColor], // 加减法
                       @"y" : [UIColor brownColor], // 辅助药物
                       };
}

// 解析内容
- (NSMutableAttributedString *)parseText:(NSMutableString *)text
{
    // 解析$x{}标记，支持嵌套标记，需要3个堆栈。
    NSMutableArray *stack1 = [NSMutableArray new]; // 存放$x{}标记的堆栈
    NSMutableArray<NodeEnd *> *stack2 = [NSMutableArray new]; // “}”出栈进栈的地方
    NSMutableArray<NodeStart *> *stack3 = [NSMutableArray new]; // 消除前2个堆栈，到此堆栈，以颠倒遍历
    
    // 逐个字符读取判断，将"$x{"和"}"标记按顺序入栈，并根据栈内元素数量算出标记词其真实偏移
    for (int i = 0; i < text.length; i++) {
        NSString *s = [text substringWithRange:NSMakeRange(i, 1)];
        if (![@"$}" containsString:s]) {
            continue;
        }
        
        // 计算标记词的起始位置偏移
        NSNumber *count = [stack1 reduceArrayWithBlock:^id(id item, NSNumber *sum) {
            return @(sum.intValue + ([item isKindOfClass:[NodeStart class]] ? 3 : 1));
        } initValue:@(0)];
        if ([s isEqualToString:@"$"]) {
            NodeStart *node = [NodeStart new];
            node.start = i - count.intValue;
            node.cls = [text substringWithRange:NSMakeRange(i+1, 1)];
            [stack1 addObject:node];
        }else if ([s isEqualToString:@"}"]) {
            NodeEnd *node = [NodeEnd new];
            node.end = i - count.intValue;
            [stack1 addObject:node];
        }
    }
    
    // 遍历完毕，即清除所有$a{}标记。
    if(!reg){
        reg = [NSRegularExpression regularExpressionWithPattern:@"[$a-z{}]" options:NSRegularExpressionCaseInsensitive error:nil];
    }
    [reg replaceMatchesInString:text options:0 range:NSMakeRange(0, text.length) withTemplate:@""];
    
    // 遍历第一个堆栈，遇到结束标记'}'，就压入stack2，遇到开始标记'$x{',就从stack2栈顶获取标记结束位置，配对完毕，将完整标记范围对象压入stack3;
    for (NSInteger i = stack1.count - 1; i >= 0; i--) {
        if ([stack1.lastObject isKindOfClass:[NodeStart class]]) {
            NodeStart *tmp = stack1.lastObject;
            tmp.end = stack2.lastObject.end;
            [stack3 addObject:tmp];
            [stack1 removeObject:tmp];
        }else{
            NodeEnd *tmp = stack1.lastObject;
            [stack2 addObject:stack1.lastObject];
            [stack1 removeObject:tmp];
        }
    }
    
    // 此处，stack3已经按从头到尾的顺序放好了所有嵌套标记的范围，可以开始渲染颜色
    NSMutableAttributedString *astr = [[NSMutableAttributedString alloc] initWithString:text];
    
    // 遍历stack3，按照标记范围逐个渲染。
    for (NSInteger i = stack3.count - 1; i >= 0; i--) {
        NodeStart *node = stack3[i];
        NSRange range = NSMakeRange(node.start, node.end - node.start);
        [astr addAttribute:NSForegroundColorAttributeName value:[AppDelegate sharedApp].colorDict[node.cls] range:range];
        if ([@"wha" containsString:node.cls]) {
            [astr addAttribute:NSFontAttributeName value:[HH2SearchConfig sharedConfig].smallFont range:range];
        }
        if ([@"uf" containsString:node.cls]){
            [astr addAttributes:@{NSUnderlineColorAttributeName:[UIColor blackColor], NSUnderlineStyleAttributeName:@(NSUnderlineStyleSingle)} range:range];
            if ([node.cls isEqualToString:@"f"]) {
                [astr addAttribute:NSStrokeWidthAttributeName value:@(-2) range:range];
                // 增加链接小窗口
            }else{
                // 增加链接小窗口
            }
        }
    }
    
    // 如果有行号，也染色
    NSRange range = [text rangeOfString:@"、"];
    if (range.location != NSNotFound) {
        [astr addAttribute:NSForegroundColorAttributeName value:[HH2SearchConfig sharedConfig].nColor range:NSMakeRange(0,range.location)];
    }
    return astr;
}

- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions {
    // Override point for customization after application launch.
    me = self;
    [self initData];
    
    [[UINavigationBar appearance] setBarTintColor:[UIColor colorWithRed:0.6 green:0.7 blue:1.0 alpha:1]];
    
    return YES;
}

- (void)applicationWillResignActive:(UIApplication *)application {
    // Sent when the application is about to move from active to inactive state. This can occur for certain types of temporary interruptions (such as an incoming phone call or SMS message) or when the user quits the application and it begins the transition to the background state.
    // Use this method to pause ongoing tasks, disable timers, and throttle down OpenGL ES frame rates. Games should use this method to pause the game.
}

- (void)applicationDidEnterBackground:(UIApplication *)application {
    // Use this method to release shared resources, save user data, invalidate timers, and store enough application state information to restore your application to its current state in case it is terminated later.
    // If your application supports background execution, this method is called instead of applicationWillTerminate: when the user quits.
}

- (void)applicationWillEnterForeground:(UIApplication *)application {
    // Called as part of the transition from the background to the inactive state; here you can undo many of the changes made on entering the background.
}

- (void)applicationDidBecomeActive:(UIApplication *)application {
    // Restart any tasks that were paused (or not yet started) while the application was inactive. If the application was previously in the background, optionally refresh the user interface.
}

- (void)applicationWillTerminate:(UIApplication *)application {
    // Called when the application is about to terminate. Save data if appropriate. See also applicationDidEnterBackground:.
}

@end
